case
分支在本章中,我们将继续探讨流量控制。在【第28章.阅读键盘输入】中,我们构建了一些简单的菜单,并构建了用于对用户选择进行操作的逻辑。为此,我们使用了一系列 if
命令来识别选择了哪些可能的选项。这种逻辑结构在程序中经常出现,以至于许多编程语言(包括shell)为多项选择决策提供了一种特殊的流控制机制。
case
在bash中,多选复合命令称为 case
。它具有以下语法:
case word in [pattern [| *pattern]...) commands ;;]... esac
如果我们查看【第28章】中的读取菜单程序,我们会看到用于对用户选择进行操作的逻辑。
xxxxxxxxxx
# read-menu: a menu driven system information program
clear
echo "
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
"
read -r -p "Enter selection [0-3] > "
if [[ "$REPLY" =~ ^[0-3]$ ]]; then
if [[ "$REPLY" == 0 ]]; then
echo "Program terminated."
exit
fi
if [[ "$REPLY" == 1 ]]; then
echo "Hostname: $HOSTNAME"
uptime
exit
fi
if [[ "$REPLY" == 2 ]]; then
df -h
exit
fi
if [[ "$REPLY" == 3 ]]; then
if [[ "$(id -u)" -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh "$HOME"
fi
exit
fi
else
echo "Invalid entry." >&2
exit 1
fi
使用 case
,我们可以用更简单的东西替换这个逻辑。
xxxxxxxxxx
# case-menu: a menu driven system information program
clear
echo "
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
"
read -r -p "Enter selection [0-3] > "
case "$REPLY" in
0) echo "Program terminated."
exit
;;
1) echo "Hostname: $HOSTNAME"
uptime
;;
2) df -h
;;
3) if [[ "$(id -u)" -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh "$HOME"
fi
;;
*) echo "Invalid entry" >&2
exit 1
;;
esac
case
命令查看 word 的值,在我们的示例中是 REPLY
变量的值,然后尝试将其与指定的模式(patterns)之一进行匹配。当找到匹配项时,将执行与指定模式关联的命令(commands)。找到匹配后,不再尝试进一步的匹配。
patterns —— 模式
case
使用的模式与路径名扩展使用的模式相同。模式以 )
字符结束。下表列出了有效模式的示例。
模式 | 描述 |
---|---|
a) | 如果 word 等于“a”,则匹配。 |
[[:alpha:]]) | 如果 word 是单个字母字符,则匹配。 |
???) | 如果 word 长度恰好为三个字符,则匹配。 |
*.txt) | 如果 word 以字符“.txt”结尾,则匹配。 |
*) | 匹配 word 的任何值。最好将此作为 case 命令中的最后一个模式,以捕获与前一个模式不匹配的 word 的任何值,即捕获任何可能的无效值。 |
以下是一个工作模式的示例:
xxxxxxxxxx
read -r -p "enter word > "
case "$REPLY" in
[[:alpha:]]) echo "is a single alphabetic character." ;;
[ABC][0-9]) echo "is A, B, or C followed by a digit." ;;
???) echo "is three characters long." ;;
*.txt) echo "is a word ending in '.txt'" ;;
*) echo "is something else." ;;
esac
也可以使用竖条字符作为分隔符组合多个模式。这创建了一个“或”条件模式。这对于处理大写和小写字符非常有用。这里有一个例子:
xxxxxxxxxx
# case-menu: a menu driven system information program
clear
echo "
Please Select:
A. Display System Information
B. Display Disk Space
C. Display Home Space Utilization
Q. Quit
"
read -r -p "Enter selection [A, B, C or Q] > "
case "$REPLY" in
q|Q)echo "Program terminated."
exit
;;
a|A)echo "Hostname: $HOSTNAME"
uptime
;;
b|B)df -h
;;
c|C)if [[ "$(id -u)" -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh "$HOME"
fi
;;
*) echo "Invalid entry" >&2
exit 1
;;
esac
在这里,我们修改了 case
菜单程序,使用字母而不是数字进行菜单选择。请注意,新模式如何允许输入大写和小写字母。
在4.0之前的bash版本中, case
只允许对成功匹配执行一个操作。匹配成功后,命令将终止。这里我们看到一个测试角色的脚本:
xxxxxxxxxx
# case4-1: test a character
read -r -n 1 -p "Type a character > "
echo
case "$REPLY" in
[[:upper:]]) echo "'$REPLY' is upper case." ;;
[[:lower:]]) echo "'$REPLY' is lower case." ;;
[[:alpha:]]) echo "'$REPLY' is alphabetic." ;;
[[:digit:]]) echo "'$REPLY' is a digit." ;;
[[:graph:]]) echo "'$REPLY' is a visible character." ;;
[[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;
[[:space:]]) echo "'$REPLY' is a whitespace character." ;;
[[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;
esac
运行此脚本会产生以下结果:
xxxxxxxxxx
[me@linuxbox ~]$ case4-1
Type a character > a
'a' is lower case.
该脚本在很大程度上有效,但如果一个字符与多个POSIX字符类匹配,则会失败。例如,字符“a”既是小写字母,也是字母,也是十六进制数字。在bash 4.0之前的版本中, case
无法匹配多个测试。现代版本的bash添加了 ;;&
符号来终止每个操作,所以现在我们可以这样做:
xxxxxxxxxx
# case4-2: test a character
read -r -n 1 -p "Type a character > "
echo
case "$REPLY" in
[[:upper:]]) echo "'$REPLY' is upper case." ;;&
[[:lower:]]) echo "'$REPLY' is lower case." ;;&
[[:alpha:]]) echo "'$REPLY' is alphabetic." ;;&
[[:digit:]]) echo "'$REPLY' is a digit." ;;&
[[:graph:]]) echo "'$REPLY' is a visible character." ;;&
[[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;&
[[:space:]]) echo "'$REPLY' is a whitespace character." ;;&
[[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;&
esac
当我们运行此脚本时,我们得到以下结果:
xxxxxxxxxx
[me@linuxbox ~]$ case4-2
Type a character > a
'a' is lower case.
'a' is alphabetic.
'a' is a visible character.
'a' is a hexadecimal digit.
添加 ;;&
语法允许 case
继续到下一个测试,而不是简单地终止。
case
命令是我们编程技巧包中的一个方便的补充。正如我们将在下一章中看到的,它是处理某些类型问题的完美工具。